using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using EnvDTE80;

namespace EventAbstractionAPI.CodeGenerator
{
    public class UIEventInjection
    {

        /// <summary>
        /// Found a file now check if it is a UI file.
        /// </summary>
        internal void InspectFile(ProjectItem item)
        {
            if (item.Document != null)
            {
                InspectCode(item.Document);
            }
        }

        internal void OverwriteFileContents(String newText, Document doc)
        {
            TextDocument textDocument = (TextDocument)doc.Object("");
            EditPoint startPoint = textDocument.StartPoint.CreateEditPoint();
            EditPoint endPoint = textDocument.EndPoint.CreateEditPoint();

            //vsEPReplaceTextOptionsKeepMarkers 	    0 	Try to keep existing markers in place.
            //vsEPReplaceTextOptionsNormalizeNewlines 	1 	Use the text buffer's default newline sequence
            //vsEPReplaceTextOptionsTabsSpaces 	        2 	Keep what is in Text and process tabs and spaces according to the Keep Tabs option in the Tools Options dialog box.
            //vsEPReplaceTextOptionsAutoFormat 	        3 	Insert Text and automatically format it.
            startPoint.ReplaceText(endPoint, newText, 2);

        }

        /// <summary>
        /// This method is called when a folder is found and will recurse till a file is found.
        /// </summary>
        /// <param name="item"></param>
        internal void GetAFile(ProjectItem item)
        {
            foreach (ProjectItem child in item.ProjectItems)
            {
                String[] projName = child.Name.Split('.');
                if (projName.Length == 1)
                {
                    GetAFile(child);
                }
                else
                {
                    //check to see if the file is a csharp code file.
                    if (projName[projName.Length - 1] == "cs")
                    {
                        InspectFile(child);
                    }
                }
            }
        }

        internal void InspectCode(Document doc)
        {
            _document = doc;

            _codeElements = doc.ProjectItem.FileCodeModel.CodeElements;

            //Get the lines that are namespace or class declarations.

            for (int i = 1; i <= _codeElements.Count; i++)
            {
                if (_codeElements.Item(i).Kind == vsCMElement.vsCMElementNamespace )
                {
                    //forevery subelement of a namespace look for classes.
                    foreach (CodeElement codeElement in _codeElements.Item(i).Children)
                    {
                        if (codeElement.Kind == vsCMElement.vsCMElementClass)
                        {
                            if (InspectClass((CodeClass2)codeElement))
                            {
                                //file has been modified
                                foreach (CodeElement elem in _codeElements)
                                {
                                    //TODO: add the using statement in for the mynamespace to the UI's
                                   // _document.ProjectItem
                                }
                            }
                        }
                    }
                }
                else if (_codeElements.Item(i).Kind == vsCMElement.vsCMElementClass)
                {
                    if (InspectClass((CodeClass2)_codeElements.Item(i)))
                    {
                        //file has been modified

                    }
                }
            }
            _codeElements.Item(_codeElements.Count);
        }

        private bool InspectClass(CodeClass2 element)
        {
            CodeFunction2 constructor = null;

            //1. check to see if class inherits Window or Form.
            if (element.Bases.Item(1).Name == "Window")
            {
            
                //2. if it does find the constructor.
                //element.Members
                //((element.Members.Item("Window1"))).StartPoint
                foreach (CodeElement elem in element.Members)
	            {
                    if (elem.Name == element.Name && elem.Kind == vsCMElement.vsCMElementFunction )
                    {
                        constructor = (CodeFunction2)elem;

                        _document.Activate();
                        TextSelection selection = (TextSelection)_document.Selection;

                        //back up the selection information so that we can use the selection and restore it back to where it was.
                        int x1 = selection.TopPoint.LineCharOffset;
                        int y1 = selection.TopPoint.Line;
                        int x2 = selection.BottomPoint.LineCharOffset;
                        int y2 = selection.BottomPoint.Line;
                        
                        //select the codelines inside the function.
                        selection.MoveToLineAndOffset(constructor.StartPoint.Line+2,1,false);
                        selection.MoveToLineAndOffset(constructor.EndPoint.Line, 1, true);

                        //3. if the CodeLoop c = new CodeLoop(); is not found we need to put it in the code.
                        //NOTE: the whitespace is trimmed.
                        if (!FindDeclaration(selection, "newEventLoop"))
                        {
                            selection.Text += "\nEventLoop c = new EventLoop();";
                        }

                        //put selection back to where it was.
                        selection.MoveToLineAndOffset(y1, x1, false);
                        selection.MoveToLineAndOffset(y2, x2, true);

                        //if a file was updated, lets say that it was by returning true.
                        return true;
                        break;
                    }
	            }
            }
            //file was not updated.
            return false;
        }

        private bool FindDeclaration(TextSelection text, String declaration)
        {
            String[] lines = text.Text.Split(';');
            bool found = false;

            String trimmedLine = "";
            
            foreach (String line in lines)
            {
                //makesure we trim all whitespace for searching.
                trimmedLine = line;
                trimmedLine = trimmedLine.Replace(" ","");

                if (trimmedLine.Contains(declaration))
                {
                    found = true;
                    break;
                }
            }

            return found;
        }

        #region Member Variables

        String _namespace = "";
        String _class = "";
        CodeElements _codeElements;
        Document _document; 

        #endregion
    }
}
